package net.lab1024.sa.admin.module.business.category.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.lab1024.sa.admin.common.DateTimeUtils;
import net.lab1024.sa.admin.common.EnumConverter;
import net.lab1024.sa.admin.common.Log;
import net.lab1024.sa.admin.common.UserImportListener;
import net.lab1024.sa.admin.config.UserContextHolder;
import net.lab1024.sa.admin.constant.SynthesisConst;
import net.lab1024.sa.admin.module.business.category.dao.RoleUserMapper;
import net.lab1024.sa.admin.module.business.category.dao.UserDao;
import net.lab1024.sa.admin.module.business.category.domain.dto.UserDTO;
import net.lab1024.sa.admin.module.business.category.domain.dto.UserExportDTO;
import net.lab1024.sa.admin.module.business.category.domain.dto.UserImportDTO;
import net.lab1024.sa.admin.module.business.category.domain.dto.UserQueryDTO;
import net.lab1024.sa.admin.module.business.category.domain.entity.RoleUser;
import net.lab1024.sa.admin.module.business.category.domain.entity.UpdatePasswordQuery;
import net.lab1024.sa.admin.module.business.category.domain.entity.User;
import net.lab1024.sa.admin.module.business.category.domain.entity.UserContext;
import net.lab1024.sa.admin.module.business.category.domain.form.ImportErrorVO;
import net.lab1024.sa.admin.module.business.category.domain.vo.ImportResultVO;
import net.lab1024.sa.admin.module.business.category.domain.vo.UserVO;
import net.lab1024.sa.admin.module.system.role.service.RoleService;
import net.lab1024.sa.admin.module.system.role.service.RoleUserService;
import net.lab1024.sa.base.common.code.UserErrorCode;
import net.lab1024.sa.base.common.domain.RequestUser;
import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.exception.BusinessException;
import net.lab1024.sa.base.module.support.apiencrypt.service.ApiEncryptService;
import net.lab1024.sa.base.module.support.securityprotect.service.SecurityPasswordService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class UserService extends ServiceImpl<UserDao, User> {

    private static final Logger logger = LoggerFactory.getLogger(UserService.class);

    @Autowired
    private UserDao userDao;

    @Resource
    private SecurityPasswordService securityPasswordService;

    @Resource
    private RoleUserService roleUserService;
    @Resource
    RoleUserMapper roleUserMapper;
    @Resource
    private RoleService roleService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private ApiEncryptService apiEncryptService;

    /**
     * 新增用户
     */
    @Log(module = "用户管理", operation = "新增用户")
    @Transactional
    public int addUser(UserDTO userDTO) {
        try {
            // 检查学工号是否已存在
            if (lambdaQuery().eq(User::getUserCode, userDTO.getUserCode()).exists()) {
                throw new BusinessException("学号已存在");
            }
            User user = new User();
            BeanUtils.copyProperties(userDTO, user);
            String userCode = userDTO.getUserCode();
            String encryptPwd = SecurityPasswordService.getEncryptPwd(userCode);
            user.setLoginPwd(encryptPwd); // 默认密码
            user.setStatus(EnumConverter.defaultStatus);
            user.setYn(EnumConverter.defaultYn);

            int userResult = userDao.insert(user);

            RoleUser roleUser = new RoleUser();
            roleUser.setRoleId(userDTO.getRole().longValue());
            roleUser.setUserId(user.getId());
            roleUser.setCreateTime(DateTimeUtils.getNow());
            roleUser.setUpdateTime(DateTimeUtils.getNow());

            int roleUserResult = roleUserMapper.insertOrIgnore(roleUser);

            return userResult > 0 && roleUserResult > 0 ? 1 : 0;
        } catch (Exception e) {
            logger.error("新增用户异常：", e);
            throw new RuntimeException("新增用户失败", e);
        }
    }

    private String generateLoginName(String userName) {
        try {
            return PinyinUtil.getPinyin(userName, "");
        } catch (Exception e) {
            logger.error("生成登录用户名异常：", e);
            return "";
        }
    }

    public Boolean downloadTemplate(HttpServletResponse response) {
        try {
            ClassPathResource resource = new ClassPathResource("template/用户导入模板.xlsx");
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + URLEncoder.encode("用户导入模板.xlsx", "UTF-8"));

            try (InputStream is = resource.getInputStream();
                 OutputStream os = response.getOutputStream()) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
            }
            return true;
        } catch (Exception e) {
            logger.error("下载导入模板异常：", e);
            return false;
        }
    }

    /**
     * 批量导入
     */
    @Log(module = "用户管理", operation = "导入用户")
    public ImportResultVO importUsers(MultipartFile file) {
        String creator = UserContextHolder.getUserContext().getUserCode();
        ImportResultVO resultVO = new ImportResultVO();

        try {
            // 1. 解析Excel
            UserImportListener listener = new UserImportListener(
                    this,
                    roleUserService,
                    roleService,
                    transactionTemplate,
                    apiEncryptService,
                    creator
            );

            EasyExcel.read(file.getInputStream(), UserImportDTO.class, listener)
                    .sheet()
                    .doRead();

            // 2. 处理导入结果
            List<UserImportListener.ImportResult> errors = listener.getErrorList();
            resultVO.setTotalCount(listener.getProcessedCount());
            resultVO.setSuccessCount(errors.isEmpty() ? resultVO.getTotalCount() : 0);
            resultVO.setErrorList(errors.stream().map(e ->
                    new ImportErrorVO(e.getRowNum(), e.getMessage())
            ).collect(Collectors.toList()));

            return resultVO;
        } catch (Exception e) {
            log.error("用户导入失败", e);

            // 3. 异常处理
            resultVO.setTotalCount(0);
            resultVO.setSuccessCount(0);
            resultVO.setErrorList(Collections.singletonList(
                    new ImportErrorVO(-1, "系统错误: " + e.getMessage())
            ));
            return resultVO;
        }
    }

    /**
     * 导出用户
     */
    @Log(module = "用户管理", operation = "导出用户")
    public void exportUsers(UserQueryDTO query, HttpServletResponse response) {
        try {
            // 1. 构建查询条件
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            if (null != query) {
                wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
                        .eq(StringUtils.isNotBlank(query.getUserCode()), User::getUserCode, query.getUserCode())
                        .eq(query.getStatus() != null, User::getStatus, query.getStatus())
                        .like(StringUtils.isNotBlank(query.getCollege()), User::getCollege, query.getCollege());
            }

            handleWrapper(wrapper, query);

            // 2. 查询数据
            List<User> users = this.list(wrapper);

            // 3. 转换为导出DTO
            List<UserExportDTO> exportList = users.stream()
                    .map(UserExportDTO::fromEntity)
                    .collect(Collectors.toList());

            // 4. 导出Excel
            String fileName = URLEncoder.encode("用户数据", StandardCharsets.UTF_8.name())
                    .replaceAll("\\+", "%20");

            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");


            EasyExcel.write(response.getOutputStream(), UserExportDTO.class)
                    .sheet("用户数据")
                    .doWrite(exportList);
        } catch (IOException e) {
            logger.error("批量导出用户异常：", e);
            throw new RuntimeException("导出失败", e);
        }
    }


    public Page<UserVO> listUsers(UserQueryDTO query) {
        Page<User> page = new Page<>(query.getPage(), query.getSize());
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .eq(StringUtils.isNotBlank(query.getUserCode()), User::getUserCode, query.getUserCode())
                .eq(query.getStatus() != null, User::getStatus, query.getStatus())
                .eq(StringUtils.isNotBlank(query.getCollege()), User::getCollege, query.getCollege())
                .eq(StringUtils.isNotBlank(query.getClassName()), User::getClassName, query.getClassName())
                .orderByDesc(User::getUpdateTime);
        handleWrapper(wrapper, query);

        Page<User> userPage = this.page(page, wrapper);
        // 转换为VO的分页对象
        Page<UserVO> voPage = new Page<>();
        // 复制分页信息
        BeanUtils.copyProperties(userPage, voPage, "records");

        // 转换记录列表
        List<UserVO> voList = convertToVO(userPage.getRecords());
        voPage.setRecords(voList);
        return voPage;
    }

    private void handleWrapper(LambdaQueryWrapper<User> wrapper, UserQueryDTO query) {
        UserContext userContext = UserContextHolder.getUserContext();
        Assert.notNull(userContext, "当前用户不存在");
        User user = userDao.selectById(userContext.getUserId());
        Assert.notNull(user, "当前用户不存在");

        if (EnumConverter.generalUserRoleCode.equals(user.getRole())) {
            wrapper.eq(true, User::getName, user.getName());
        } else {
            wrapper.like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName());
        }
    }


    private List<UserVO> convertToVO(List<User> users) {
        try {
            return users.stream().map(user -> {
                UserVO vo = new UserVO();
                BeanUtils.copyProperties(user, vo);
                vo.setUserId(user.getId());
                // 转换枚举值为显示名称
                vo.setGender(EnumConverter.convertSexCodeToName(user.getGender()));
                vo.setCollege(EnumConverter.convertCollegeCodeToName(user.getCollege()));
                vo.setPosition(EnumConverter.convertPositionCodeToName(user.getPosition()));
                vo.setRole(EnumConverter.convertRoleCodeToName(user.getRole()));
                vo.setStatus(EnumConverter.convertStatusCodeToName(user.getStatus()));
                vo.setCollege(EnumConverter.convertCollegeCodeToName(user.getCollege()));
                vo.setClassName(EnumConverter.convertClassCodeToName(user.getClassName()));
                return vo;
            }).collect(Collectors.toList());

        } catch (Exception e) {
            logger.error("转化 UserVO 异常", e);
            return null;
        }
    }


    public User getDetail(Long id, String userCode) {
        try {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            if (id != null) {
                wrapper.eq(User::getId, id);
            } else if (StringUtils.isNotBlank(userCode)) {
                wrapper.eq(User::getUserCode, userCode);
            } else {
                logger.info("从UserContextHolder获取查询用户参数");
                UserContext userContext = UserContextHolder.getUserContext();
                wrapper.eq(User::getId, userContext.getUserId());
                wrapper.eq(User::getUserCode, userContext.getUserCode());
            }
            return userDao.selectOne(wrapper);
        } catch (Exception e) {
            logger.error("查询用户异常：", e);
            throw new RuntimeException("查询用户异常", e);
        }
    }

    /**
     * 修改用户
     */
    @Log(module = "用户管理", operation = "修改用户")
    public Integer updateUser(UserDTO query) {
        validateUpdateParam(query);
        User user = new User();
        BeanUtils.copyProperties(query, user);
        // 1. 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        if (null != query) {
            wrapper.like(null != query.getId(), User::getId, query.getId())
                    .eq(StringUtils.isNotBlank(query.getUserCode()), User::getUserCode, query.getUserCode());
        }

        user.setUpdateTime(new Date());
        return userDao.update(user, wrapper);
    }

    private void validateUpdateParam(UserDTO query) {
        Assert.isFalse(null == query, "入参为空");
        Assert.isFalse(null == query.getId() && StringUtils.isBlank(query.getUserCode()), "参数错误,userId和userCode不能都为空");
    }

    public User getUser(String userCode) {
        try {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            if (StringUtils.isNotBlank(userCode)) {
                wrapper.eq(User::getUserCode, userCode);
            } else {
                throw new BusinessException("查询参数错误");
            }
            return this.getOne(wrapper);
        } catch (Exception e) {
            logger.error("查询用户异常：", e);
            throw new RuntimeException("查询用户异常", e);
        }
    }

    /**
     * 重置密码
     */
    @Log(module = "用户管理", operation = "重置密码")
    public Integer resetPassword(Long userId) {
        validateParam(userId);
        User user = new User();
        User userEntity = userDao.selectById(userId);

        user.setId(userId);
        String password = SecurityPasswordService.getEncryptPwd(String.valueOf(userEntity.getUserCode()));
        user.setLoginPwd(password);
        // 1. 构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        user.setUpdateTime(new Date());

        wrapper.eq(true, User::getId, userId);

        return userDao.update(user, wrapper);
    }

    private void validateParam(Long userId) {
        Assert.notNull(userId, "用户Id不能为空");
        User user = userDao.selectById(userId);
        Assert.notNull(user, String.format("userId=%s 的用户不存在", userId));
    }

    /**
     * 更新密码
     */
    @Log(module = "用户管理", operation = "修改用户")
    @Transactional(rollbackFor = Throwable.class)
    public ResponseDTO<String> updatePassword(RequestUser requestUser, UpdatePasswordQuery updatePasswordQuery) {
        Long userId = updatePasswordQuery.getUserId();
        User user = userDao.selectById(userId);
        if (user == null) {
            return ResponseDTO.error(UserErrorCode.DATA_NOT_EXIST);
        }

        // 校验原始密码
        if (!SecurityPasswordService.matchesPwd(updatePasswordQuery.getOldPassword(), user.getLoginPwd())) {
            return ResponseDTO.userErrorParam("原密码有误，请重新输入");
        }

        // 新旧密码相同
        if (Objects.equals(updatePasswordQuery.getOldPassword(), updatePasswordQuery.getNewPassword())) {
            return ResponseDTO.userErrorParam("新密码与原始密码相同，请重新输入");
        }

        // 校验密码复杂度
        ResponseDTO<String> validatePassComplexity = securityPasswordService.validatePasswordComplexity(updatePasswordQuery.getNewPassword());
        if (!validatePassComplexity.getOk()) {
            return validatePassComplexity;
        }

        // 根据三级等保规则，校验密码是否重复
        ResponseDTO<String> passwordRepeatTimes = securityPasswordService.validatePasswordRepeatTimes(requestUser, updatePasswordQuery.getNewPassword());
        if (!passwordRepeatTimes.getOk()) {
            return ResponseDTO.error(passwordRepeatTimes);
        }

        // 更新密码
        String newEncryptPassword = SecurityPasswordService.getEncryptPwd(updatePasswordQuery.getNewPassword());
        User updateEntity = new User();
        updateEntity.setId(userId);
        updateEntity.setLoginPwd(newEncryptPassword);
        userDao.updateById(updateEntity);

        // 保存修改密码密码记录
        securityPasswordService.saveUserChangePasswordLog(requestUser, newEncryptPassword, user.getLoginPwd());

        return ResponseDTO.ok();
    }


}